home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MacWorld 1999 November
/
Macworld (1999-11).dmg
/
Updaters
/
WhiteCap 3.0.4
/
WhiteCap Source.sit
/
WhiteCap Source
/
WhiteCap.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1999-08-30
|
17KB
|
785 lines
#include "WhiteCap.h"
#include "EgOSUtils.h"
#if EG_MAC
#include <Menus.h>
#include <Windows.h>
#include <Palettes.h>
#if MACAMP
#include "MacAMP_Visual.h"
extern VPInfoBlock gPlugInfo;
#endif
#if SOUNDJAM
#include "VisFramework.h"
#endif
#include "CEgFileSpec.h"
#include "Sample.h"
#include "CEgIOFile.h"
#define __setupPort GDHandle saveDev; \
GrafPtr savePort; \
::GetGWorld( (GWorldPtr*)(&savePort), &saveDev ); \
::SetPort( mOSPort );
#define __restorePort ::SetGWorld( (GWorldPtr)(savePort), saveDev );
#endif
#if EG_WIN
#define __setupPort
#define __restorePort
#include "RectUtils.h"
#include "vis.h"
extern winampVisModule gWCModule;
#endif
WhiteCap::WhiteCap( CEgFileSpec& inPluginsFolder, void* inRefCon ) :
mPrefs( "WhiteCap Preferences", true ),
mConfigs( cNoDuplicates_CaseInsensitive, cSortLowToHigh) {
CEgFileSpec spec;
UtilStr name, str;
mPrefs.Load();
if ( mPrefs.GetPref( 'Vers' ) != 304 ) {
mTransitionLo = 4;
mTransitionHi = 15;
mSlideShowInterval = 20000; // Factory: 20 secs per config
mScrnSaverDelay = 15 * 60000; // Factory: 15 mins before screen saver kicks in
}
else {
mTransitionLo = mPrefs.GetPref( 'TrLo' );
mTransitionHi = mPrefs.GetPref( 'TrHi' );
mSlideShowInterval = mPrefs.GetPref( 'Slde' ) * 1000;
mScrnSaverDelay = mPrefs.GetPref( 'SSvr' ) * 1000 * 60;
}
mRefCon = inRefCon;
mInSlideShowMode = true;
mAtFullScreen = false;
mOSPort = NULL;
mNumWorlds = 0;
mFrameCount = 0;
mFrameCountStart = EgOSUtils::CurTimeMS();
mCurConfigNum = -1;
mNextShapeChange = 0x7FFFFFFF;
mFramesPer10Secs = 400;
mDoingSetPortWin = false;
mMouseWillAwaken = false;
if ( mSlideShowInterval > 0 )
mNextShapeChange = mFrameCountStart + mSlideShowInterval;
BuildConfigList( inPluginsFolder );
}
WhiteCap::~WhiteCap() {
SetFullScreen( false );
// Rewrite the prefs to disk...
mPrefs.SetPref( 'Vers', 304 );
mPrefs.SetPref( 'Slde', mSlideShowInterval / 1000 );
mPrefs.SetPref( 'SSvr', mScrnSaverDelay / 60000 );
mPrefs.SetPref( 'TrHi', mTransitionHi );
mPrefs.SetPref( 'TrLo', mTransitionLo );
#if MACAMP || WINAMP
Rect r;
#if MACAMP
GetWinRect( r );
#else
// Very annoying: in Win32, the call to GetWindowRect() is returning garbage in ~WhiteCap(), so just
// forget about saving the window position for now in windows :_(
r = mWinRectHolder;
#endif
mPrefs.SetPref( 'wTop', r.top );
mPrefs.SetPref( 'wLft', r.left );
mPrefs.SetPref( 'wBot', r.bottom );
mPrefs.SetPref( 'wRgt', r.right );
#endif
mPrefs.Store();
// Delete all the existing worlds
for ( int i = 0; i < mNumWorlds; i++ )
delete mWorld[ i ];
#if DATA_DUMP
CEgIOFile oFile;
CEgFileSpec spec( "WhiteCap Dump" );
oFile.open( &spec );
if ( oFile.noErr() ) {
oFile.Write( &mDumpData );
}
#endif
}
bool WhiteCap::PtInTitle( Point inPt ) {
int i;
Rect r;
GetWinRect( r );
inPt.v -= r.top;
inPt.h -= r.left;
for ( i = 0; i < mNumWorlds; i++ ) {
if ( ::PtInRect( inPt, &mWorld[ i ] -> mTitleRect ) )
return true;
}
return false;
}
void WhiteCap::SelectConfig() {
long i, sel, idxOffset;
Point pt;
UtilStr name;
EgOSUtils::GetMouse( pt );
// We want to add the menu item "(Start Slide Show)" when we're not in slide show mode
if ( mInSlideShowMode )
idxOffset = 0;
else
idxOffset = 1;
// If we're in full screen mode, show the mouse ptr
if ( mAtFullScreen )
EgOSUtils::ShowCursor();
#if EG_MAC
MenuHandle menu;
__setupPort
// If we found a world, show a popup menu to select from the configs
menu = ::NewMenu( 123, "\p " );
if ( ! mInSlideShowMode ) {
::AppendMenu( menu, "\p " );
::SetMenuItemText( menu, 1, "\p(Start Slide Show)" );
}
for ( i = 1; mConfigs.FetchSpecName( i, name ); i++ ) {
::AppendMenu( menu, "\p " );
::SetMenuItemText( menu, i + idxOffset, name.getPasStr() );
}
::CheckItem( menu, mCurConfigNum + idxOffset, true );
::InsertMenu( menu, -1 );
sel = ::PopUpMenuSelect( menu, pt.v - 6, pt.h, mCurConfigNum + idxOffset );
::DeleteMenu( 123 );
::DisposeMenu( menu );
sel = ( sel & 0xFFFF0000 ) ? (sel & 0xFFFF) : 0;
#endif
#if EG_WIN
long flags;
HMENU myMenu = ::CreatePopupMenu();
if ( ! mInSlideShowMode )
::AppendMenu( myMenu, MF_ENABLED | MF_STRING, 1, "(Start Slide Show)" );
for ( i = 1; mConfigs.FetchSpecName( i, name ); i++ ) {
flags = MF_ENABLED | MF_STRING;
if ( mCurConfigNum == i )
flags |= MF_CHECKED;
::AppendMenu( myMenu, flags, i + idxOffset, name.getCStr() );
}
sel = ::TrackPopupMenuEx( myMenu, TPM_RETURNCMD, pt.h, pt.v, mOSPort, NULL );
::DestroyMenu( myMenu );
if ( mAtFullScreen )
::SetCapture( mOSPort );
#endif
if ( sel > 0 ) {
if ( mInSlideShowMode )
mInSlideShowMode = false;
else {
if ( sel == 1 ) {
mInSlideShowMode = true;
mConfigPlayList.Randomize();
sel = mConfigPlayList.Fetch( 1 ); }
else
sel--;
}
LoadConfig( sel );
}
__restorePort
}
void WhiteCap::SetFullScreen( bool inFullScreen ) {
#if SOUNDJAM
VisHandlerData* handlerData = (VisHandlerData*) mRefCon;
if ( handlerData ) {
if ( mAtFullScreen != inFullScreen ) {
if ( PlayerSetFullScreen(handlerData->appCookie, handlerData->playerProc, inFullScreen) == noErr)
mAtFullScreen = inFullScreen;
}
}
#else
bool ok;
int dispNum;
Point size;
if ( inFullScreen && ! mAtFullScreen ) {
if ( PixPort::FullscreenAvail() ) {
__setupPort
// Update the positon of our win
GetWinRect( mWinRectHolder );
dispNum = PixPort::GetOwningDisplay( *((Point*) &mWinRectHolder) );
#if MACAMP
if ( gPlugInfo.ma -> EnterFullScreen )
gPlugInfo.ma -> EnterFullScreen( cPluginAuthor, cWhiteCapID );
::HideWindow( mOSPort );
#endif
#if WINAMP
::ShowWindow( gWCModule.hwndParent, SW_HIDE | SW_MINIMIZE );
#endif
// Put in pref wheather to try 32 before 16 bit color?
ok = mPort.InitFullscreen( dispNum, size, mOSPort );
if ( ok ) {
mDispRect.left = 0;
mDispRect.top = 0;
mDispRect.right = size.h;
mDispRect.bottom = size.v;
ResizeWorlds();
mAtFullScreen = true;
mFullscreenStartTime = EgOSUtils::CurTimeMS();
// If we don't expire them, there'll be an ugly gap for the time it took to go fullscreen
for ( int i = 0; i < mNumWorlds; i++ )
mWorld[ i ] -> ExpireSamples();
}
else {
#if MACAMP
if ( gPlugInfo.ma -> ExitFullScreen )
gPlugInfo.ma -> ExitFullScreen();
::ShowWindow( mOSPort );
#endif
#if WINAMP
::ShowWindow( gWCModule.hwndParent, SW_SHOWNORMAL );
#endif
}
__restorePort
} }
else if ( ! inFullScreen && mAtFullScreen ) {
// Restore the window
mPort.Deactivate();
#if MACAMP
if ( gPlugInfo.ma -> ExitFullScreen )
gPlugInfo.ma -> ExitFullScreen();
#endif
__setupPort
SetWinPort( mOSPort, &mWinRectHolder );
__restorePort
mLastMousePt.h -= 55;
mAtFullScreen = false;
#if WINAMP
::ShowWindow( gWCModule.hwndParent, SW_SHOWNORMAL );
#endif
}
#endif
if ( inFullScreen ) {
// Changing the port (and the resolution) may change the mouse cords
EgOSUtils::GetMouse( mLastMousePt );
// Default: mouse movement will not exit fullscreen mode
mMouseWillAwaken = false;
}
#if EG_MAC
::FlushEvents( 0xFFFF, 0 );
#endif
}
void WhiteCap::GetWinRect( Rect& outRect ) {
if ( mOSPort ) {
#if EG_MAC
outRect = (**(((CGrafPtr) mOSPort)->portPixMap)).bounds;
outRect.left *= -1;
outRect.top *= -1;
outRect.right = outRect.left + mOSPort -> portRect.right;
outRect.bottom = outRect.top + mOSPort -> portRect.bottom;
#elif EG_WIN
RECT wr;
::GetWindowRect( mOSPort, &wr );
outRect.left = wr.left;
outRect.top = wr.top;
outRect.right = wr.right;
outRect.bottom = wr.bottom;
#endif
}
else
SetRect( &outRect, 0, 0, 0, 0 );
}
void WhiteCap::RecordSample( long inCurTime, float inSpectrum[] ) {
#if DATA_DUMP
mDumpData.Append( &inCurTime, 4 );
mDumpData.Append( inSpectrum, sizeof( float ) * NUM_SAMPLE_BINS );
// Give the sample data to the window if we're not just dumping the sound data
#else
// Loop thru all the panes in the window
for ( int i = 0; i < mNumWorlds; i++ ) {
// Tell the world to take a new sound sample (if it wants)
mWorld[ i ] -> RecordSample( inCurTime, inSpectrum );
}
#endif
}
void WhiteCap::Draw() {
Rect r;
Point pt;
long i, time = EgOSUtils::CurTimeMS();
// Load a random waveshape every so often, and randomize things
if ( time > mNextShapeChange && mInSlideShowMode ) {
// Load the next config in the (randomized) config list...
i = mConfigPlayList.FindIndexOf( mCurConfigNum );
// Make a new play list if we've reached the end of the list...
if ( i >= mConfigPlayList.Count() ) {
mConfigPlayList.Randomize();
i = 0;
}
LoadConfig( mConfigPlayList.Fetch( i + 1 ) );
}
EgOSUtils::GetMouse( pt );
// Check the mouse pos and record it as active if its been moved.
if ( pt.h != mLastMousePt.h || pt.v != mLastMousePt.v ) {
mLastMousePt = pt;
mLastActiveTime = time;
if ( mAtFullScreen && mMouseWillAwaken )
SetFullScreen( false );
}
// If we've been idle for too long, go into full screen mode and let mouse movement wake us
if ( time - mLastActiveTime > mScrnSaverDelay && mScrnSaverDelay > 0 ) {
mLastActiveTime = time;
SetFullScreen( true );
mMouseWillAwaken = true;
}
__setupPort
// Tell the PixPort we're about to start accessing it big time
mPort.BeginFrame();
// Loop thru all the panes in the window and the the whitecap wave thingy
time = EgOSUtils::CurTimeMS();
for ( i = 0; i < mNumWorlds; i++ ) {
// Tell the world to update itself to the offscreen draw port
mWorld[ i ] -> Render( time, mPort, r );
// If we're drawing to a window, draw it right away
if ( mPort.IsFullscreen() )
mPort.UnionDirtyRect( &r );
else
mPort.CopyBits( mOSPort, &r, &r );
}
// In the case we're using DrawSpocks, the cur port is the 'screen' port
GrafPtr port = mOSPort;
#if EG_MAC
if ( mPort.IsFullscreen() )
::GetPort( &port );
#endif
// For each pane/world, draw the title string...
for ( i = 0; i < mNumWorlds; i++ ) {
mWorld[ i ] -> DrawConfigName( port );
// In mac land, we may need to tell the port to refresh the given rect on the screen
#if EG_MAC
mPort.UnionDirtyRect( &mWorld[ i ] -> mTitleRect );
#endif
}
//__setupPort
#if EG_MAC
// If the option key is down, show the frame rate
unsigned char km[16];
::GetKeys( (unsigned long*) km );
i = 58;
if ( ((km[ i >> 3 ] >> (i & 7)) & 1) != 0 ) {
// Calc frame rate, show the current computation for frames
if ( time - mFrameCountStart >= 1500 ) {
mFramesPer10Secs = 10000 * mFrameCount / ( time - mFrameCountStart );
mFrameRate.Assign( mFramesPer10Secs );
mFrameRate.Insert( mFrameRate.length() - 1, ".", 1 );
mFrameCountStart = time;
mFrameCount = 0;
}
mFrameCount++;
Rect r = { 0, 0, 20, 40 };
::EraseRect( &r );
::MoveTo( 5, 15 );
::DrawString( mFrameRate.getPasStr() );
if ( mPort.IsFullscreen() )
mPort.UnionDirtyRect( &r );
}
#endif
mPort.EndFrame();
__restorePort
}
#define ___max( a, b ) (( (a) > (b) ) ? (a) : (b))
#define ___maxSide( r ) (___max( r.right - r.left, r.bottom - r.top ))
void WhiteCap::SetWinPort( GrafPtr inWin, const Rect* inRect ) {
Rect r;
// mDoingSetPortWin == true is a signal that another thread is in SetWinPort()
if ( mDoingSetPortWin )
return;
mDoingSetPortWin = true;
if ( inRect )
r = *inRect;
// If an invalid win rect, fix it, you monkey!
if ( r.right - r.left < 20 || r.bottom - r.top < 20 || ! inRect ) {
r.top = mPrefs.GetPref( 'wTop' );
r.left = mPrefs.GetPref( 'wLft' );
r.right = mPrefs.GetPref( 'wRgt' );
r.bottom = mPrefs.GetPref( 'wBot' );
#if EG_MAC
// Invalidate the window if it's offscreen...
RgnHandle deskRgn = ::GetGrayRgn();
::SectRect( &(*deskRgn) -> rgnBBox, &r, &r );
#endif
}
// If no prefs avail (or an older version, use factory rect) or a bad win, hard code a size
if ( mPrefs.GetPref( 'Vers' ) != 30 || r.right - r.left < 20 || r.bottom - r.top < 20 ) {
r.top = 50;
r.left = 15;
r.right = 480;
r.bottom = 380;
}
long x = r.right - r.left;
long y = r.bottom - r.top;
#if EG_MAC
::MoveWindow( inWin, r.left, r.top, true );
::SizeWindow( inWin, x, y, true );
::ShowWindow( inWin );
#else
RECT cr;
// Resize the window and find the rgn we have to work with
::MoveWindow( inWin, r.left, r.top, x, y, false );
::GetClientRect( inWin, &cr );
x = cr.right - cr.left;
y = cr.bottom - cr.top;
#endif
// The playpen for WhiteCap will be the entire window client rgn (what we'll give SetPort)
Rect portRect;
::SetRect( &portRect, 0, 0, x, y );
SetPort( inWin, portRect, false );
// Signal that this thread is done with SetPortWin()
mDoingSetPortWin = false;
}
void WhiteCap::SetPort( GrafPtr inPort, const Rect& inRect, bool inFullScreen ) {
long x = inRect.right - inRect.left;
long y = inRect.bottom - inRect.top;
mOSPort = inPort;
mPort.Init( x, y );
mDispRect = inRect;
mAtFullScreen = inFullScreen;
__setupPort
// If we're brand spankin new, show at least 1 config, you monkey
if ( mNumWorlds == 0 )
LoadConfig( mCurConfigNum );
__restorePort
for ( int i = 0; i < mNumWorlds; i++ )
mWorld[ i ] -> ExpireSamples();
ResizeWorlds();
}
void WhiteCap::ResizeWorlds() {
Rect r;
long i, j, bestLen, cutLen, best;
// Resize the panes in this window...
mWorld[ 0 ] -> SetPaneRect( mDispRect );
for ( i = 1; i < mNumWorlds; i++ ) {
// Find the best world to cut in half
for ( bestLen = -1, j = 0; j < i; j++ ) {
cutLen = ___maxSide( (*mWorld[ j ] -> PaneRect()) );
if ( cutLen >= bestLen ) {
bestLen = cutLen;
best = j;
}
}
// Change the pane rect of both the unplaced world and the world that's about to get cut in half
r = *(mWorld[ best ] -> PaneRect());
if ( r.right - r.left == bestLen ) {
r.right -= bestLen / 2;
mWorld[ best ] -> SetPaneRect( r );
OffsetRect( &r, bestLen / 2, 0 );
mWorld[ i ] -> SetPaneRect( r ); }
else {
r.bottom -= bestLen / 2;
mWorld[ best ] -> SetPaneRect( r );
OffsetRect( &r, 0, bestLen / 2 );
mWorld[ i ] -> SetPaneRect( r );
}
}
}
void WhiteCap::RefreshRect( const Rect& inUpdate ) {
for ( int i = 0; i < mNumWorlds; i++ ) {
mWorld[ i ] -> RefreshRect( inUpdate );
}
}
void WhiteCap::LoadConfig( int inConfigNum ) {
const CEgFileSpec* configSpec;
int i, exists;
long oldNumWorlds = mNumWorlds;
long transitionTime = EgOSUtils::Rnd( mTransitionLo * 1000, mTransitionHi * 1000 );
mNumWorlds = 0;
mCurConfigNum = -1;
// Fetch the spec for our config file or folder
configSpec = mConfigs.FetchSpec( inConfigNum );
if ( configSpec ) {
// If we have a folder or file...
exists = configSpec -> Exists();
if ( exists == 2 ) {
CEgFileSpec spec;
bool startOver = true;
while ( EgOSUtils::GetNextFile( *configSpec, spec, startOver, false ) ) {
if ( mNumWorlds >= oldNumWorlds )
mWorld[ mNumWorlds ] = new WhiteCapWorld;
mWorld[ mNumWorlds ] -> Init( &spec, transitionTime );
mNumWorlds++;
startOver = false;
}
}
// Know what to put a check mark next to in the popup menu
mCurConfigNum = inConfigNum;
}
// If it's only one config file or there's no spec specifed
if ( mNumWorlds == 0 ) {
mNumWorlds = 1;
if ( mNumWorlds > oldNumWorlds )
mWorld[ 0 ] = new WhiteCapWorld;
mWorld[ 0 ] -> Init( configSpec, transitionTime );
}
for ( i = mNumWorlds; i < oldNumWorlds; i++ )
delete mWorld[ i ];
// If the cursor was spun, init it back to the ptr
EgOSUtils::ResetCursor();
// Make sure all WhiteCap's configs live happily inside the allowed draw area
ResizeWorlds();
// Set the time for when we load a random config automatically
mNextShapeChange = EgOSUtils::CurTimeMS() + mSlideShowInterval + transitionTime;
// Very annoying: in Win32, the call to GetWindowRect() is returning garbage in ~WhiteCap(), so just
// forget about saving the window position for now in windows :_(
#if EG_WIN
if ( ! mAtFullScreen )
GetWinRect( mWinRectHolder );
#endif
if ( mAtFullScreen )
EgOSUtils::HideCursor();
}
void WhiteCap::BuildConfigList( CEgFileSpec& inPluginsFolder ) {
CEgFileSpec spec;
bool startOver = true;
int i;
while ( EgOSUtils::GetNextFile( inPluginsFolder, spec, startOver, false ) ) {
mConfigs.AddCopy( spec );
startOver = false;
}
startOver = true;
while ( EgOSUtils::GetNextFile( inPluginsFolder, spec, startOver, true ) ) {
mConfigs.AddCopy( spec );
startOver = false;
}
// Always have a item called (Default) in the config list
UtilStr def( "(Default)" );
spec.Assign( inPluginsFolder );
if ( ! mConfigs.Lookup( def ) ) {
spec.Rename( def );
mConfigs.AddCopy( spec );
}
mCurConfigNum = mConfigs.Lookup( def );
// Build a random config 'play' list
mConfigPlayList.RemoveAll();
for ( i = mConfigs.Count(); i > 0; i-- )
mConfigPlayList.Add( i );
mConfigPlayList.Randomize();
}